home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
dev
/
sun4.md
/
devJaguarHBA.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
57KB
|
1,870 lines
/*
* devJaguarHBA.c --
*
* Driver for the Interphase V/SCSI 4210 Jaguar SCSI host bus adapter
* for SUN's running Sprite.
*
* Copyright 1989 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/dev/sun4.md/devJaguarHBA.c,v 9.10 92/10/23 15:03:42 elm Exp $ SPRITE (Berkeley)";
#endif /* not lint */
#include <sprite.h>
#include <mach.h>
#include <jaguar.h>
#include <jaguarDefs.h>
#include <dev.h>
#include <devInt.h>
#include <sys/scsi.h>
#include <scsiHBA.h>
#include <scsiDevice.h>
#include <vmMach.h>
#include <sync.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <bstring.h>
/*
* WARNING -- WARNING --- WARNING --- WARNING
* The code and data structures in this file are carefully coded to
* run on machines that don't do autobus sizing and unaligned memory fetches.
*
* Please keep in mind the following:
*
* 1) Although structures like struct JaguarMem appear to be C data structures
* they are used to describe the interface to the Jaguar controller board.
* Because these data structures need to be understood by both the
* host CPU and the Jaguar's processor changes should only occur when
* the Jaguar's firmware changes the interface.
* 2) The Jaguar is accessed in the short IO spaces on the VME bus. This
* means that the data path is only 16 bits wide. This means that
* unless your processor does autobus sizing (ie convert 32 bit accesses
* into 2 16 bit access if the bus width is 16 bit) only shorts should
* be stored into JaguarMem. Note: SPARC (the sun4) doesn't do autobus
* sizing.
* 3) Beware of padding introduced by the compiler. For example, if the
* Jaguar documentation says:
* 2 bytes length
* 4 bytes buffer address
* and you declare a C structure to be
* struct {
* short length;
* char *bufAddr;
* }
* Things will break (on sun4) because the compiler will added 2 bytes of
* padding to insure that the (char *) starts on the (4 byte) word boundry.
* The routine CheckSizes in this file tries to catch errors of this
* form.
*/
/*
* The Jaguar occupies 2 Kbytes on the VME short I/O space (DEV_VME_D16A16).
* The image of these 2 Kbytes is described by the structure
* struct JaguarMem. Some of the members of this structure like
* the mcsb, mce, cmdQueue,and css must occur at fixed offsets from the start
* of the 2K region. The other items in JaguarMem are placed at fixed offsets
* to simplify the driver and may be moved around.
*/
/*
* Constants that define the layout of JaguarMem.
*
* SIZE_JAUGAR_MEM - The size of the dual ported memory accessible to the
* host.
* NUM_CQE - Number of entries in the command queue. This limits
* the number of unacknowledged commands that we can submit
* to the Jaguar. For a lack of a better number, 8.
*
* NUM_SG_ELEMENTS - Number of scatter/gather elements allocated in the
* juguar memory. Scatter/gather elements don't needed
* to be allocated in Jaguar memory but it is convenient
* to do so. Otherwise they would have to be allocated
* or mapped into DVMA space.
*
* MEM_PAD Number of bytes of free space not allocated in the
* structure JaguarMem. Note that MEM_PAD is a function
* of NUM_SG_ELEMENTS and NUM_CQE.
* NUM_WORK_QUEUES Number of work queues to use.
*
*/
#define SIZE_JAUGAR_MEM (2*1024)
#define NUM_CQE 8
#define NUM_SG_ELEMENTS 64
#define NUM_WORK_QUEUES 14
#define MEM_PAD (SIZE_JAUGAR_MEM - sizeof(JaguarMCSB) - \
(NUM_CQE+1)*(sizeof(JaguarIOPB)+sizeof(JaguarCQE)) - \
sizeof(JaguarCRB) - sizeof(JaguarCCSB) - \
NUM_SG_ELEMENTS * sizeof(JaguarSG))
typedef struct JaguarMem {
JaguarMCSB mcsb; /* Master Control and Status Block. 16 Bytes */
JaguarCQE mce; /* Master Command Entry Queue. 12 Bytes */
JaguarCQE cmdQueue[NUM_CQE]; /* Command Queue. 12*NUM_CQE Bytes */
JaguarIOPB masterIOPB; /* IOPB used for the MCE. 64 Bytes */
JaguarIOPB iopbs[NUM_CQE]; /* IOPB for cmdQueue. 64*NUM_CQE Bytes*/
JaguarSG sgElements[NUM_SG_ELEMENTS];
/* Scatter/gather elements for host. */
char padding[MEM_PAD]; /* Padding to force css field into
* correct offset. */
JaguarCRB crb; /* Command response block. 76 Bytes */
JaguarCCSB css; /* Contoller specific Space. 120 Bytes */
} JaguarMem;
/*
* Many of the Jaguar's data structures need addresses as offsets into the
* JaguarMem. To deal with this offsets:
*
* POINTER_TO_OFFSET() - Convert a host pointer to an offset.
* OFFSET_TO_POINTER() - Convert an offset to a host pointer.
*
*/
#define POINTER_TO_OFFSET(ptr, memPtr) ((unsigned)(ptr) - (unsigned)(memPtr))
#define OFFSET_TO_POINTER(offset, memPtr) ((char *)(memPtr)+(unsigned)(offset))
/*
* workq (s) -
* From the documentation it appears that for the Jaguar to efficiently
* talk to a device a workq must be configured for the device. The
* documentation doesn't mention a cost associated with having many
* unused workq so it is tempting to create all 14 work queues at
* initialization time. The reason for not doing this is that certain
* options such as parity and workq priority is determined during
* work queue initialization.
*
*/
/* Forward declaration. */
typedef struct Controller Controller;
/*
* Device - The data structure containing information about a device. One of
* these structure is kept for each attached device. Note that is structure
* is casted into a ScsiDevice and returned to higher level software.
* This implies that the ScsiDevice must be the first field in this
* structure.
*/
typedef struct Device {
ScsiDevice handle; /* Scsi Device handle. This is the only part
* of this structure visible to higher
* level software. MUST BE FIRST FIELD IN STRUCTURE. */
int bus; /* Bus number of device. */
int targetID; /* TargetID of device. */
int unitAddress; /* Jaguar address of this device. */
int workQueue; /* Jaguar workqueue allocated for this device. */
int numActiveCmds; /* Number of commands enqueued on the HBA for this
* command. */
Controller *ctrlPtr; /* Controller to which device is attached. */
/*
* The following part of this structure is
* used to handle SCSI commands that return
* CHECK status. To handle the REQUEST SENSE
* command we must: 1) Save the state of the current
* command into the "struct FrozenCommand". 2) Submit
* a request sense command */
struct FrozenCommand {
ScsiCmd *scsiCmdPtr; /* The frozen command. */
unsigned char statusByte; /* It's SCSI status byte, Will always have
* the check bit set. */
int amountTransferred; /* Number of bytes transferred by this
* command. */
} frozen;
} Device;
typedef struct CmdAction {
int action; /* Action to be performed when command completes.
* See below for list of actions. */
int dmaBufferLen; /* DMA buffer length. */
Address dmaBuffer; /* DMA buffer for device. */
ClientData actionArg; /* Argument for action. */
} CmdAction;
/*
* Upon command termination. The interrupt handler can be instructed to
* perform serveral possible actions.
*
* UNUSED_ACTION - This action buffer is used.
* FILL_IN_CRB_ACTION - Fill in the specified pointer with the completion
* CRB.
* SCSI_CMD_ACTION - This command is a SCSI command. Invoke RequestDone
* function.
* IS_WAIT_ACTION() - TRUE if the action requires the command to be
* executed synchronously.
* NUM_ACTIONS - Number of action buffers to allocate per controller.
* This number must be creater than the number of
* devices * the number of queued commands.
*/
#define UNUSED_ACTION 0x0
#define FILL_IN_CRB_ACTION 0x1
#define SCSI_CMD_ACTION 0x2
#define IS_WAIT_ACTION(action) ((action)== FILL_IN_CRB_ACTION)
#define NUM_ACTIONS 64
/*
* Controller - The Data structure describing a Jaguar controller. One
* of these structures exists for each active Jaguar HBA on the system. Each
* controller may have from zero to 14 devices attached to it.
*/
struct Controller {
volatile JaguarMem *memPtr; /* Pointer to the registers
of this controller. */
volatile JaguarCQE *nextCQE; /* Next available CQE. */
Boolean workQueue0Busy; /* Work Queue 0 is being used. */
char *name; /* String for error message for this controller. */
Sync_Semaphore mutex; /* Lock protecting controller's data structures. */
DevCtrlQueues devQueues; /* Device queues for devices attached to this
* controller. */
Sync_Condition ctrlCmdWait; /* Wait condition for syncronous command
* to finish. */
Sync_Condition ctrlQueue0Wait; /* Wait condition for exclustive access
* to workqueue 0. */
int intrLevel; /* VME interrupt level for controller. */
int intrVector; /* VME interrupt vector for controller in
* the format expected by the Jaguar. */
int nextActionBuffer; /* Next cmdAction buffer to allocated. */
CmdAction cmdAction[NUM_ACTIONS]; /* Action to be performed when command
* completes. */
Device *devices[NUM_WORK_QUEUES]; /* Pointers to the device attached. The
* index is the workQueue number - 1. */
};
#define MAX_JAGUAR_CTRLS 16
static Controller *Controllers[MAX_JAGUAR_CTRLS];
static int devJaguarDebug = 0;
/*
* The follow data structure is used by the Sprite kernel debugger to
* examine Jaguar memory. See routine GetJaguarMem.
*/
#ifndef lint
static JaguarMem DebugJaguarMem;
#endif
/*
* Constants for the sun implementation.
* DMA_BURST_COUNT - The number of VME DMA transfers performed in a
* single burst before releasing the bus. A value of
* 0 uses the maximum of 128 32-bit transfers.
* JAGUAR_WORD_ADDRESS_MODIFIER
* JAGUAR_BLOCK_ADDRESS_MODIFIER - The Address space modifier and transfer type
* for Jaguar DMA. We choose 32 bit normal mode
* transfers with a A24 bit supervisor data address
* modifier (0x3d or 0x3f).
* MAX_CMDS_QUEUED - Maximum number of command to queue per device.
* SELECTION_TIMEOUT - Timeout value for selecting a device in 1 millisecond
* ticks. We choose 1 second timeout.
* RESELECTION_TIMEOUT - Timeout value for a device re-selecting in 32
* milliseconds ticks. 0 means infinite.
* VME_TIMEOUT - Timeout value for VME access. 0 means 100 milliseconds.
* DEV_MAX_DMA_SIZE - Maximum size of a DMA request to this device.
*/
#define DMA_BURST_COUNT 0
#define JAGUAR_WORD_ADDRESS_MODIFIER (JAGUAR_32BIT_MEM_TYPE | \
JAGUAR_NORMAL_MODE_XFER | 0x0d)
#define JAGUAR_BLOCK_ADDRESS_MODIFIER (JAGUAR_32BIT_MEM_TYPE | \
JAGUAR_BLOCK_MODE_XFER | 0x0f)
#define MAX_CMDS_QUEUED 2
#define SELECTION_TIMEOUT 1000
#define RESELECTION_TIMEOUT 0
#define VME_TIMEOUT 0
#define DEV_MAX_DMA_SIZE (128*1024)
#define VME_INTERRUPT_PRIORITY 3 /* was 2, changed by elm */
/*
* Macros for reading and writing 32 bit values from the short IO space.
*/
#define READ_LONG(var) (((var)[0]<<16)|((var)[1]))
#define SET_LONG(var,value) \
(((var)[0] = ((value)>>16)),((var)[1]=(0xffff&(value))))
static void LockWorkq0();
static void UnLockWorkq0();
static void CopyFromJaguarMem();
static void CopyToJaguarMem();
static void ZeroJaguarMem();
static Boolean WaitForBitSet();
static void FillInScsiIOPB();
static void RequestDone();
static void StartNextRequest();
static char *ErrorString();
static Boolean SendJaguarCmd();
/*
*----------------------------------------------------------------------
*
* GetJaguarMem --
*
* Make a copy of the Jaguar's memory image in DebugJaguarMem. This
* routine is used because the Sprite debugger can't read data
* in short VME space. It is only called by the debugger.
*
* Results:
* The address of the Jaguar memory copied.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
#ifndef lint
static unsigned int
GetJaguarMem(ctrlNum)
int ctrlNum; /* Controller to number to act upon. */
{
CopyFromJaguarMem(Controllers[ctrlNum]->memPtr, &DebugJaguarMem, 2048);
return (unsigned int) Controllers[ctrlNum]->memPtr;
}
#endif
/*
*----------------------------------------------------------------------
*
* WaitForResponseBlock --
*
* Wait for a Jaguar command to complete.
*
* Results:
* TRUE always. Should probably sent up a timeout handler.
*
* Side effects:
* None.
*----------------------------------------------------------------------
*/
static void
WaitForResponseBlock(ctrlPtr,crbPtr)
Controller *ctrlPtr; /* Controller to which command
* was submitted. */
volatile JaguarCRB *crbPtr; /* Command Response Block to be filled in by
* interrupt handler. */
{
while (!(crbPtr->status & JAGUAR_CRB_BLOCK_VALID)) {
Sync_MasterWait(&(ctrlPtr->ctrlCmdWait), &ctrlPtr->mutex,FALSE);
}
return;
}
/*
*----------------------------------------------------------------------
*
* InitializeWorkq --
*
* Send Jaguar the command to initialize the specified work queue
* with the given parameters and options.
*
* NOTE: This routine assumes that the error recovery method used
* in the driver would be to Freeze the Work queue upon error.
*
* Results:
* TRUE is queue was initialized, FALSE otherwise.
*
* Side effects:
* A work queue initialize command is send to the controller.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
static Boolean
InitializeWorkq(ctrlPtr,workqNum, parity, priority)
Controller *ctrlPtr; /* Controller to initalize Work Queue on. */
int workqNum; /* Workq to initialize must be 1-14. */
Boolean parity; /* TRUE - parity check should be enabled. When
* communicated thru this workq. FALSE if not.
*/
int priority; /* The priority level of this workq. (1-14) */
{
JaguarIOPB inMemIOPB;
volatile register JaguarIOPB *iopb = &inMemIOPB;
JaguarCRB crb;
Boolean good;
if (devJaguarDebug > 3) {
printf("%s: Initializing workQueue %d ...\n", ctrlPtr->name, workqNum);
}
/*
* Build the appropriate Initialized Work Queue Command IOPB
* and send it to workQ 0. We must use workq 0 for this comand.
*/
bzero((char *) &crb, sizeof(crb));
bzero((char *) iopb, sizeof(*iopb));
iopb->command = JAGUAR_INIT_WORK_QUEUE_CMD;
iopb->options = JAGUAR_IOPB_INTR_ENA;
iopb->intrVector = ctrlPtr->intrVector;
iopb->intrLevel = ctrlPtr->intrLevel;
iopb->cmd.workQueueArg.number = workqNum;
iopb->cmd.workQueueArg.options =
JAGUAR_WQ_INIT_QUEUE |
JAGUAR_WQ_FREEZE_QUEUE |
#ifdef youWantItNotToWork
/*
* Setting the JAGUAR_WQ_PARITY_ENABLE bit in the workQueue options
* causes the HBA to quit working.
*/
(parity ? JAGUAR_WQ_PARITY_ENABLE : 0);
#else
0;
#endif
iopb->cmd.workQueueArg.slots = NUM_CQE;
iopb->cmd.workQueueArg.priority = priority;
/*
* Send the command into work queue zero. SendJaguarCmd will do the
* waiting for us and return
*/
MASTER_LOCK(&ctrlPtr->mutex);
good = SendJaguarCmd(ctrlPtr, 0, iopb, FILL_IN_CRB_ACTION,(ClientData)&crb);
MASTER_UNLOCK(&ctrlPtr->mutex);
if (!good) {
panic("%s: Initialize WorkQ %d did not finished.\n",ctrlPtr->name,
workqNum);
return (FALSE);
}
if (crb.status & (JAGUAR_CRB_ERROR|JAGUAR_CRB_EXCEPTION)) {
printf("%s: Initialize WorkQ %d failed with error 0x%s %s\n",
ctrlPtr->name, workqNum, crb.iopb.returnStatus,
ErrorString(crb.iopb.returnStatus));
return (FALSE);
}
if (devJaguarDebug > 3) {
printf("%s: WorkQueue %d initialized\n", ctrlPtr->name, workqNum);
}
return (TRUE);
}
/*
*----------------------------------------------------------------------
*
* PerformDiagnostics --
*
* Perform an extensive diagnostics test of the JaguarHBA.
*
*
* Results:
* TRUE if the controller passes the test. FALSE if controller is broken.
*
* Side effects:
* The docuementation says the JAGUAR_DIAG_CMD can take several
* minutes to execute. I let it run for 45 mins and it still didn't
* finish.
*
*----------------------------------------------------------------------
*/
static Boolean
PerformDiagnostics(memPtr, name)
volatile JaguarMem *memPtr; /* Pointer to VME Short IO space of HBA. */
char *name; /* Name of the controller for error messges. */
{
register volatile JaguarCQE *cqe;
register volatile JaguarIOPB *iopb;
register volatile JaguarCRB *crb;
Boolean good = TRUE;
cqe = &(memPtr->mce);
iopb = &(memPtr->masterIOPB);
crb = &(memPtr->crb);
/*
* The command we send is the Perform Diagnostics command.
*/
ZeroJaguarMem((short *)iopb, sizeof(iopb));
iopb->command = JAGUAR_DIAG_CMD;
{
register unsigned int status;
/*
* Send the command off and wait for response.
*/
cqe->controlReg = JAGUAR_CQE_GO_BUSY;
if (!WaitForBitSet(&(crb->status),JAGUAR_CRB_BLOCK_VALID,100000)) {
panic("%s diag timeout. status = 0x%x\n", name,
crb->status);
return FALSE;
}
/*
* Check for happy completion status.
*/
status = crb->status;
if (!(status & JAGUAR_CRB_COMMAND_COMPLETE)) {
panic("%s diag cmd didn't complete, status 0x%x\n",
name, status);
return FALSE;
}
if (status & (JAGUAR_CRB_ERROR|JAGUAR_CRB_EXCEPTION)) {
printf("%s diag cmd error 0x%x, status 0x%x\n",
name, crb->iopb.returnStatus, status);
good = FALSE;
}
if (crb->iopb.cmd.diagArg.romTest != 0xffff) {
printf("%s failed ROM test, result = 0x%x\n", name ,
crb->iopb.cmd.diagArg.romTest);
good = FALSE;
}
if (crb->iopb.cmd.diagArg.scrRamTest != 0xffff) {
printf("%s failed Scratch pad RAM test, result = 0x%x\n", name ,
crb->iopb.cmd.diagArg.scrRamTest);
good = FALSE;
}
if (crb->iopb.cmd.diagArg.bufRamTest != 0xffff) {
printf("%s failed Buffer RAM test, result = 0x%x\n", name ,
crb->iopb.cmd.diagArg.bufRamTest);
good = FALSE;
}
if (crb->iopb.cmd.diagArg.eventRamTest != 0xffff) {
printf("%s failed Evant RAM test, result = 0x%x\n", name ,
crb->iopb.cmd.diagArg.eventRamTest);
good = FALSE;
}
if (crb->iopb.cmd.diagArg.priPort != 0xffff) {
printf("%s failed Primary SCSI port test, result = 0x%x\n", name ,
crb->iopb.cmd.diagArg.priPort);
good = FALSE;
}
if (crb->iopb.cmd.diagArg.secPort != 0xffff) {
printf("%s failed Secondary SCSI port test, result = 0x%x\n", name ,
crb->iopb.cmd.diagArg.secPort);
good = FALSE;
}
/*
* If we got here the controller init cmd successfully completed.
* Acknowledge the command to release the CRB.
*/
crb->status = 0;
}
return good;
}
/*
*----------------------------------------------------------------------
*
* InitializeJaguar --
*
* Initialize the Jaguar HBA and ready it for commands.
*
* This routine assumes that the controller is MASTER_LOCKed()
*
* Results:
* TRUE if the controller initializes. FALSE if controller is broken.
*
* Side effects:
* Controller is reset and initialized. This causes the SCSI bus(es) to
* be reset and any in progress commands aborted.
*
*----------------------------------------------------------------------
*/
static Boolean
InitializeJaguar(memPtr, name, intrLevel, intrVector)
volatile JaguarMem *memPtr; /* Pointer to VME Short IO space of HBA. */
char *name; /* Name of the controller for error messges. */
int intrLevel; /* VME Interrupt level for HBA. */
int intrVector; /* VME Interrupt vector for HBA. */
{
register volatile JaguarMCSB *mcsb = &(memPtr->mcsb);
register volatile JaguarCQE *cqe;
register volatile JaguarIOPB *iopb;
register volatile JaguarCRB *crb;
/*
* Start off with a clean slate by reseting the board. Documentation
* says must keep reset bit set at least 50 microseconds.
* We give it 1 millesecond of reset .
*/
mcsb->control = JAGUAR_MCR_RESET;
MACH_DELAY(1000);
mcsb->control = 0;
/*
* Wait for the Jaugar to signal Board OK. Board OK not valid for
* 100 microseconds after reset. We give it 1 millisecond to be happy.
*
* One millisecond does not appear to be long enough because the
* OK bit is still not set. We increase the amount to 1 second to
* make sure.
*/
MACH_DELAY(1000*1000);
if (!(mcsb->status & JAGUAR_MSR_BOARD_OK)) {
panic("Warning: %s board not OK, status = 0x%x\n", name, mcsb->status);
return (FALSE);
}
/*
* Initialize the rest of the MCSB. Currently we wont deal with
* queue available interrupts so we turn them off.
*/
mcsb->queueAvail = 0;
mcsb->queueHead = 0;
mcsb->reserved[0] = mcsb->reserved[1] = mcsb->reserved[2] = 0;
/*
* Initialize the Command Queue (CQ). To do this we clear out the
* CQE's and point them at their IOPBs. Clear out the IOPBs too.
*/
{
int i;
cqe = memPtr->cmdQueue;
iopb = memPtr->iopbs;
for (i = 0; i < NUM_CQE; i++, cqe++, iopb++) {
cqe->controlReg = 0;
cqe->iopbOffset = POINTER_TO_OFFSET(iopb,memPtr);
cqe->iopbLength = sizeof(JaguarIOPB)/4;
cqe->commandTag[0] = 0;
cqe->commandTag[1] = 0;
cqe->workQueue = 0;
cqe->reserved = 0;
}
ZeroJaguarMem((short *) iopb, sizeof(JaguarIOPB) * NUM_CQE);
}
/*
* Clear the area to be used as the Command Response Block.
*/
crb = &(memPtr->crb);
ZeroJaguarMem((short *) crb, sizeof(JaguarCRB));
/*
* Initialize the Master Control Enter (MCE) so we can send commands
* to the board.
*/
cqe = &(memPtr->mce);
iopb = &(memPtr->masterIOPB);
cqe->controlReg = 0;
cqe->iopbOffset = POINTER_TO_OFFSET(iopb,memPtr);
cqe->iopbLength = sizeof(JaguarIOPB)/4;
cqe->workQueue = 0;
cqe->reserved = 0;
cqe->commandTag[0] = 0;
cqe->commandTag[1] = 0;
ZeroJaguarMem((short *)iopb , sizeof(JaguarIOPB));
if (devJaguarDebug > 9) {
if (!PerformDiagnostics(memPtr, name)) {
printf("Warning: %s failed diagnostics\n");
}
}
/*
* The first command we send is the Initialize controller command.
* In order to send this command we must build a Controller
* Initialization Block (CIP) in JaguarMem. Since this block is
* not needed after initialization, we overlay the scatter sgElements
* gather vectors with the CIP.
*/
{
volatile JaguarCIB *cib = (volatile JaguarCIB *) (memPtr->sgElements);
ZeroJaguarMem((short *) cib, sizeof(JaguarCIB));
cib->numQueueSlots = NUM_CQE;
cib->dmaBurstCount = DMA_BURST_COUNT;
cib->normalIntrVector = JAGUAR_INTR_VECTOR(intrLevel,intrVector);
cib->errorIntrVector = JAGUAR_INTR_VECTOR(intrLevel,intrVector);
cib->priTargetID = JAGUAR_DEFAULT_BUS_ID;
cib->secTargetID = JAGUAR_DEFAULT_BUS_ID;
cib->offsetCRB = POINTER_TO_OFFSET(&(memPtr->crb),memPtr);
SET_LONG(cib->scsiSelTimeout, SELECTION_TIMEOUT);
SET_LONG(cib->scsiReselTimeout, RESELECTION_TIMEOUT);
SET_LONG(cib->vmeTimeout, VME_TIMEOUT);
ZeroJaguarMem((short *)iopb, sizeof(iopb));
iopb->command = JAGUAR_INIT_HBA_CMD;
iopb->addrModifier = JAGUAR_BOARD_MEM_TYPE;
SET_LONG(iopb->bufferAddr,POINTER_TO_OFFSET(cib,memPtr));
}
{
register unsigned int status;
/*
* Send the command off and wait for response.
*/
cqe->controlReg = JAGUAR_CQE_GO_BUSY;
if (!WaitForBitSet(&(crb->status),JAGUAR_CRB_BLOCK_VALID,10000)) {
panic("%s init controller timeout. status = 0x%x\n", name,
crb->status);
return FALSE;
}
/*
* Check for happy completion status.
*/
status = crb->status;
if (!(status & JAGUAR_CRB_COMMAND_COMPLETE)) {
panic("%s init ctrl cmd didn't complete, status 0x%x\n",
name, status);
return FALSE;
}
if (status & (JAGUAR_CRB_ERROR|JAGUAR_CRB_EXCEPTION)) {
panic("%s init ctrl cmd error 0x%x, status 0x%x\n",
name, iopb->returnStatus, status);
return FALSE;
}
/*
* If we got here the controller init cmd successfully completed.
* Acknowledge the command to release the CRB.
*/
crb->status = 0;
/*
* Start Queue Mode operation and wait for ack.
*/
mcsb->control |= JAGUAR_MCR_START_QUEUES;
if (!WaitForBitSet(&(crb->status),JAGUAR_CRB_BLOCK_VALID,10000)) {
panic("%s start queue mode timeout.\n",name);
return FALSE;
}
status = crb->status;
if (!(status & JAGUAR_CRB_QUEUE_START)) {
panic("%s start queue mode bad status 0x%x.\n",name,status);
return FALSE;
}
crb->status = 0;
}
/*
* Be neat! Clear out the rest of the Jaguar memory.
*/
ZeroJaguarMem((short *) (memPtr->sgElements),
NUM_SG_ELEMENTS * sizeof(JaguarSG));
ZeroJaguarMem((short *) (memPtr->padding), MEM_PAD);
/*
* Notifiy the world that we are alive by printing our Controller
* Configuration Status Block. This will be useful if someone ask
* the question: "What rev PROMs are you running?"
*/
{
JaguarCCSB css;
/*
* Note we copy the CCSB from the board because passing pointers
* into Jaguar Memory to printf would be a no-no because we
* don't have control of the type memory references done by
* printf.
*/
CopyFromJaguarMem((short *)&(memPtr->css),(short *)&css, sizeof(css));
printf("%s firmware (%3s-%c-%3s) %c%c/%c%c/%c%c ",
name, css.code, css.variation, css.firmwareLevel,
css.firmwareDate[0], css.firmwareDate[1],
css.firmwareDate[2], css.firmwareDate[3],
css.firmwareDate[6], css.firmwareDate[7]);
printf("%dK RAM bus0ID %d bus1ID %d\n", css.bufferRAMsize,
css.primaryID, css.secondaryID);
}
return TRUE;
}
static struct errorString {
int code;
char *string ;
} errorStrings[] = JAGUAR_ERROR_CODES;
#define NUM_ERROR_STRINGS (sizeof(errorStrings) / sizeof(errorStrings[0]))
/*
*----------------------------------------------------------------------
*
* ErrorString --
*
* Return a string describing a Jaguar error code.
*
* Results:
* An error string.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static char *
ErrorString(returnStatus)
unsigned short returnStatus;
{
int code = returnStatus & 0xff;
int i;
for (i = 0; i < NUM_ERROR_STRINGS; i++) {
if (errorStrings[i].code == code) {
return errorStrings[i].string;
}
}
return "Unknown error";
}
/*
*----------------------------------------------------------------------
*
* MapJaguarToSpriteErrorCode --
*
* Map a jaguar return error code to a Sprite ReturnStatus value.
*
* Results:
* A sprite return status value.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
MapJaguarToSpriteErrorCode(status)
unsigned short status;
{
int code = status & 0xff;
if (code == 0) {
return SUCCESS;
}
/*
* Error codes between 0x20 and 0x30 are VME bus type errors.
*/
if (code >= 0x20 && code < 0x30) {
return DEV_DMA_FAULT;
}
return DEV_HARD_ERROR;
}
/*
*----------------------------------------------------------------------
*
* SendScsiCommand --
*
* Enqueue a SCSI command into the Jaguar command queue.
*
* Results:
*
*
* Side effects:
*
*----------------------------------------------------------------------
*/
static Boolean
SendScsiCommand( devPtr, scsiCmdPtr)
Device *devPtr; /* Jaguar device for command. */
ScsiCmd *scsiCmdPtr; /* SCSI command to send. */
{
volatile JaguarIOPB iopbMem;
Boolean retVal;
/*
* Check to see if we can use normal PASS_THRU command or must be
* PASS_THRU_EXT extented.
*/
if (scsiCmdPtr->commandBlockLen <= sizeof(iopbMem.cmd.scsiArg.cmd)) {
/*
* We can use a simple PASS_THRU command.
*/
FillInScsiIOPB(devPtr, scsiCmdPtr, &iopbMem);
retVal = SendJaguarCmd(devPtr->ctrlPtr, devPtr->workQueue, &iopbMem,
SCSI_CMD_ACTION, (ClientData) scsiCmdPtr);
} else {
/*
* Can't handle PASS_THRU_EXT command yet.
*/
panic("%s: SCSI command too large, %d bytes.\n",devPtr->ctrlPtr->name,
scsiCmdPtr->commandBlockLen);
return (FALSE);
}
return (retVal);
}
/*
*----------------------------------------------------------------------
*
* DevJaguarIntr --
*
* Process a Jaguar interrupt by processing the entry in the ctrl's
* Command Response Block and reseting the controller.
*
* Results:
* TRUE if an interrupt was process. FALSE if not interrupt was
* present on this controller.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Boolean
DevJaguarIntr(clientData)
ClientData clientData; /* Controller to process. */
{
register volatile JaguarCRB *crb;
unsigned int status;
unsigned int returnStatus;
CmdAction *actionPtr;
register Controller *ctrlPtr;
ctrlPtr = (Controller *) clientData;
crb = &(ctrlPtr->memPtr->crb);
/*
* Check the controller's CRB. If BLOCK_VALID is we have some sort
* of condition present.
*/
status = crb->status;
if (!(status & JAGUAR_CRB_BLOCK_VALID)) {
return (FALSE);
}
/*
* Classify the condition. If should be one of the following:
* 1) A command just completed - COMMAND_COMPLETE.
* 2) Queue mode was started - QUEUE_START
* 3) A command queue entry became available. - QUEUE_AVAILABLE
*
* QUEUE_START interrupts are uninteresting to us. No processing
* is necessary. QUEUE_AVAILABLE are also uninteresting and
* unless I don't understand something impossible because we
* never set anything in the IQAR on the MCSB. Because these
* interrupt are boring we processing them first.
*/
if (status & (JAGUAR_CRB_QUEUE_START|JAGUAR_CRB_QUEUE_AVAILABLE)){
/*
* Clear the interrupt.
*/
crb->status = 0;
return (TRUE);
}
if (!(status & JAGUAR_CRB_COMMAND_COMPLETE)) {
printf("Warning: %s unknown interrupt, status = 0x%x\n",
ctrlPtr->name, status);
crb->status = 0;
return (TRUE);
}
/*
* Since all work queues should be setup with FREEZE on error and not
* ABORT on error, take aborted commands very seriously.
*/
if (status & JAGUAR_CRB_ABORTED) {
panic("%s command aborted, status = 0x%x!!!\n",
ctrlPtr->name,status);
crb->status = 0;
return (TRUE);
}
/*
* We have a real COMMAND_COMPLETION. Read the tag out of the
* return CQE to find the action to be performed. Also, read the
* return status from the IOPB returned.
*/
actionPtr = &(ctrlPtr->cmdAction[crb->commandTag[0]]);
/*
* Release the device's DMA space.
*/
if (!VmMachIsXbusMem(actionPtr->dmaBuffer) && actionPtr->dmaBufferLen > 0) {
if (((unsigned)actionPtr->dmaBuffer) & 0x80000000) {
VmMach_32BitDMAFree(actionPtr->dmaBufferLen,
actionPtr->dmaBuffer);
} else {
VmMach_DMAFree(actionPtr->dmaBufferLen, actionPtr->dmaBuffer);
}
}
returnStatus = crb->iopb.returnStatus;
/*
* Check to see an error.
*/
if (actionPtr->action & FILL_IN_CRB_ACTION) {
CopyFromJaguarMem((short *)crb,(short *) actionPtr->actionArg,
sizeof(*crb));
}
if (actionPtr->action & SCSI_CMD_ACTION) {
ScsiCmd *scsiCmdPtr = (ScsiCmd *) actionPtr->actionArg;
Device *devPtr = (Device *) (ctrlPtr->devices[crb->workQueue-1]);
ReturnStatus spriteStatus = SUCCESS;
int transferCount = READ_LONG(crb->iopb.maxXferLen);
if (returnStatus & 0xff) {
/*
* Error code 0x34 means that the transfer count didn't match
* the number of bytes transfers. We don't consider this an
* error.
*/
if ((returnStatus & 0xff) != 0x34) {
spriteStatus = MapJaguarToSpriteErrorCode(returnStatus);
transferCount = 0;
}
if (spriteStatus != SUCCESS) {
printf("Warning: Device %s HBA error 0x%x: %s\n",
devPtr->handle.locationName, returnStatus,
ErrorString(returnStatus));
}
}
devPtr->numActiveCmds--;
RequestDone(devPtr, scsiCmdPtr, spriteStatus,
(returnStatus >> 8) & 0xff, transferCount);
StartNextRequest(devPtr);
}
if (IS_WAIT_ACTION(actionPtr->action)) {
Sync_MasterBroadcast(&(ctrlPtr->ctrlCmdWait));
}
actionPtr->action = UNUSED_ACTION;
crb->status = 0;
return (TRUE);
}
/*
*----------------------------------------------------------------------
*
* SendJaguarCmd --
*
* Enter a Jaguar command into the specified Jaguar work Queue.
*
* Results:
* TRUE if the command was started. FALSE otherwise.
*
* Side effects:
* Those of Jaguar commands.
*
*----------------------------------------------------------------------
*/
static Boolean
SendJaguarCmd(ctrlPtr, workQueue, iopbPtr, action, actionArg)
Controller *ctrlPtr; /* Controller to enter command. */
int workQueue; /* Command destination workq number. */
volatile JaguarIOPB *iopbPtr; /* Jaguar IOPB command block. */
int action; /* Action to be performed on completion. */
ClientData actionArg; /* Argument to action. */
{
volatile JaguarMem *memPtr = ctrlPtr->memPtr;
volatile JaguarCQE *cqe;
volatile JaguarIOPB *iopb;
Boolean noDMA = FALSE;
unsigned int addr;
/*
* Work queue is special because it has only one entry. To keep from
* overrunning it we must observe a locking protocol.
*/
if (workQueue == 0) {
LockWorkq0(ctrlPtr);
}
/*
* Get the next Command Queue entry stepping the next available queue
* entry pointer around the circular queue.
*/
cqe = ctrlPtr->nextCQE++;
if ((ctrlPtr->nextCQE - memPtr->cmdQueue) >= NUM_CQE) {
ctrlPtr->nextCQE = memPtr->cmdQueue;
}
if (cqe->controlReg & JAGUAR_CQE_GO_BUSY) {
panic("%s: Command Queue Full\n", ctrlPtr->name);
}
addr = (unsigned) READ_LONG(iopbPtr->bufferAddr);
{
/*
* If the address has the lowest hit set then it was already
* pointing into the DMA space so we set the dmaBufferLen
* to zero to cause the interrupt handle not to free it.
*/
if (addr & 1) {
addr = addr & ~1;
SET_LONG(iopbPtr->bufferAddr,addr);
noDMA = TRUE;
}
}
/*
* Find to IOPB we hardwired this CQE to point at.
*/
iopb = memPtr->iopbs + (cqe - memPtr->cmdQueue);
/*
* Fill in the on-board iopb with the one our caller passed us.
*/
CopyToJaguarMem((short *) iopbPtr, (short *)iopb, sizeof(*iopb));
cqe->workQueue = workQueue;
/*
* Inform interrupt handler of the action we want on completion.
* To do this we must allocate a cmdAction buffer. We store the
* index into cmdAction in the cqe's commandTag to allow the
* interrupt handler to associated it with the command.
*/
{
CmdAction *actionPtr;
while (ctrlPtr->cmdAction[ctrlPtr->nextActionBuffer].action
!= UNUSED_ACTION) {
ctrlPtr->nextActionBuffer++;
if (ctrlPtr->nextActionBuffer >= NUM_ACTIONS) {
ctrlPtr->nextActionBuffer = 0;
}
}
actionPtr = &(ctrlPtr->cmdAction[ctrlPtr->nextActionBuffer]);
actionPtr->action = action;
/*
* If the address has the lowest hit set then it was already
* pointing into the DMA space so we set the dmaBufferLen
* to zero to cause the interrupt handle not to free it.
*/
if (noDMA) {
actionPtr->dmaBufferLen = 0;
} else {
actionPtr->dmaBufferLen = READ_LONG(iopbPtr->maxXferLen);
}
if ((unsigned)addr & 0x80000000) {
actionPtr->dmaBuffer = (Address) (addr);
} else {
actionPtr->dmaBuffer = (Address) (addr + VMMACH_DMA_START_ADDR);
}
actionPtr->actionArg = actionArg;
cqe->commandTag[0] = ctrlPtr->nextActionBuffer;
}
/*
* Inform the controller that the command is ready.
*/
cqe->controlReg = JAGUAR_CQE_GO_BUSY;
/*
* If the caller specified a CRB we wait for the response.
*/
if(IS_WAIT_ACTION(action)) {
WaitForResponseBlock(ctrlPtr,(volatile JaguarCRB *) actionArg);
}
if (workQueue == 0) {
UnLockWorkq0(ctrlPtr);
}
return (TRUE);
}
/*
*----------------------------------------------------------------------
*
* LockWorkq0 --
*
* Grap exclusive access to work queue 0 of the specified controller.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
LockWorkq0(ctrlPtr)
Controller *ctrlPtr;
{
while (ctrlPtr->workQueue0Busy) {
Sync_MasterWait(&(ctrlPtr->ctrlQueue0Wait), &ctrlPtr->mutex,FALSE);
}
ctrlPtr->workQueue0Busy = TRUE;
}
/*
*----------------------------------------------------------------------
*
* UnLockWorkq0 --
*
* Release exclusive access to work queue 0 of the specified controller.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
UnLockWorkq0(ctrlPtr)
Controller *ctrlPtr;
{
ctrlPtr->workQueue0Busy = FALSE;
Sync_MasterBroadcast(&(ctrlPtr->ctrlQueue0Wait));
}
/*
*----------------------------------------------------------------------
*
* CopyFromJaguarMem --
*
* Copy a block from Jaguar Memory to host memory.
* NOTE:
* This routine assumes that both the block and the host
* memory region are aligned on a 2 byte boundry and are
* an even number of bytes long.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
CopyFromJaguarMem(blockPtr, hostPtr, blockSize)
register short *blockPtr; /* Block in Jaguar Memory to copy. */
register short *hostPtr; /* Address in host memory. */
register int blockSize; /* Size of block in bytes. */
{
/*
* We do the copy 2 bytes at a time because the Jaguar is in 16 bit
* data IO space of the VME bus.
*/
while (blockSize > 0) {
*(hostPtr++) = *(blockPtr++);
blockSize -= sizeof(short);
}
}
/*
*----------------------------------------------------------------------
*
* CopyToJaguarMem --
*
* Copy a block to Jaguar Memory from host memory.
* NOTE:
* This routine assumes that both the block and the host
* memory region are aligned on a 2 byte boundry and are
* an even number of bytes long.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
CopyToJaguarMem(blockPtr, hostPtr, blockSize)
register short *blockPtr; /* Block in Jaguar Memory to copy. */
register short *hostPtr; /* Address in host memory. */
register int blockSize; /* Size of block in bytes. */
{
/*
* We do the copy 2 bytes at a time because the Jaguar is in 16 bit
* data IO space of the VME bus.
*/
while (blockSize > 0) {
*(hostPtr++) = *(blockPtr++);
blockSize -= sizeof(short);
}
}
/*
*----------------------------------------------------------------------
*
* ZeroJaguarMem --
*
* Zero a block of Jaguar board memory.
*
* NOTE: This routine assumes the block is aligned on a 2 byte boundry and
* is an even number of bytes long.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
ZeroJaguarMem(blockPtr, blockSize)
register short *blockPtr; /* Pointer to block to zero. */
register int blockSize; /* Number of bytes in the block. */
{
/*
* We do the zeroing 2 bytes at a time because the Jaguar is in 16 bit
* data IO space of the VME bus.
*/
while (blockSize > 0) {
*(blockPtr++) = 0;
blockSize -= sizeof(short);
}
}
/*
*----------------------------------------------------------------------
*
* WaitForBitSet --
*
* Wait for a bit to become set in a Jaguar word.
*
*
* Results:
* TRUE if the bit appears. FALSE if we timeout.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Boolean
WaitForBitSet(wordPtr, bit, maxCount)
register volatile unsigned short *wordPtr; /* Word to check. */
register unsigned short bit; /* Bit to check for. */
int maxCount; /* Number of 100 microseconds to check
* before giving up. */
{
/*
* Timeout after waiting one second, poll every 100 microseconds.
*/
for(; maxCount > 0; maxCount--) {
if (*wordPtr & bit) {
return (TRUE);
}
MACH_DELAY(100);
}
return ((*wordPtr & bit) != 0);
}
/*
*----------------------------------------------------------------------
*
* CheckSizes --
*
* Check the sizes of the Jaguar structure declarations to insure they
* are consistent with the controller's ideas. This routine is
* intended to catch padding introduced by the compiler that may
* break the driver's code. This checking should really be done at
* compile time but run time checking is better nothing.
*
* Results:
* TRUE if the sizes checked are ok, FALSE otherwise.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Boolean
CheckSizes()
{
return (
(SIZE_JAUGAR_MEM == sizeof(JaguarMem)) &&
(JAGUAR_MCSB_SIZE == sizeof(JaguarMCSB)) &&
(JAGUAR_CQE_SIZE == sizeof(JaguarCQE)) &&
(JAGUAR_CCSB_SIZE == sizeof(JaguarCCSB)) &&
(JAGUAR_CIB_SIZE == sizeof(JaguarCIB)) &&
(JAGUAR_MAX_IOBP_SIZE == sizeof(JaguarIOPB)) &&
(JAGUAR_CRB_SIZE == (sizeof(JaguarCRB) - sizeof(JaguarIOPB))) &&
(JAGUAR_SG_SIZE == sizeof(JaguarSG))
);
}
/*
*----------------------------------------------------------------------
*
* FillInScsiIOPB --
*
* Fill in a Jaguar IOPB with a SCSI PASS-THRU command to send
* the specified scsi command block to the specified device.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
FillInScsiIOPB(devPtr, scsiCmdPtr, iopbPtr)
Device *devPtr; /* Target device for command. */
ScsiCmd *scsiCmdPtr; /* SCSI command being sent. */
volatile JaguarIOPB *iopbPtr; /* IOPB to be filled in . */
{
Address addr;
int amod;
bzero((char *)iopbPtr, sizeof(JaguarIOPB));
iopbPtr->command = JAGUAR_PASS_THRU_CMD;
iopbPtr->options = JAGUAR_IOPB_INTR_ENA |
(scsiCmdPtr->dataToDevice ? JAGUAR_IOPB_TO_HBA : 0);
iopbPtr->intrVector = devPtr->ctrlPtr->intrVector;
iopbPtr->intrLevel = devPtr->ctrlPtr->intrLevel;
if (VmMachIsXbusMem(scsiCmdPtr->buffer)) {
amod = JAGUAR_BLOCK_ADDRESS_MODIFIER;
addr = scsiCmdPtr->buffer;
} else {
amod = JAGUAR_WORD_ADDRESS_MODIFIER;
if (scsiCmdPtr->bufferLen > 0) {
/*
* If the address is already in DMA space we do not have to
* map it. We set the lowest bit of the address to inform
* the interrupt handler not to free it.
*/
if (((unsigned)scsiCmdPtr->buffer)<(unsigned)VMMACH_DMA_START_ADDR){
addr = VmMach_32BitDMAAlloc(scsiCmdPtr->bufferLen,
scsiCmdPtr->buffer);
} else {
addr = (Address) (((unsigned) scsiCmdPtr->buffer) | 1);
}
} else {
addr = (Address) VMMACH_DMA_START_ADDR;
}
if ((unsigned) addr >= (unsigned)VMMACH_DMA_START_ADDR) {
addr -= VMMACH_DMA_START_ADDR;
}
}
iopbPtr->addrModifier = amod;
SET_LONG(iopbPtr->bufferAddr, (unsigned)addr);
SET_LONG(iopbPtr->maxXferLen, scsiCmdPtr->bufferLen);
iopbPtr->cmd.scsiArg.unitAddress = devPtr->unitAddress;
bcopy(scsiCmdPtr->commandBlock, (char *) iopbPtr->cmd.scsiArg.cmd,
scsiCmdPtr->commandBlockLen);
}
/*
*----------------------------------------------------------------------
*
* ScsiErrorProc --
*
* This function retrieves the Sense data from a device and
* Unfreezes the queue to the device.
*
* Results:
* None.
*
* Side effects:
* A REQUEST SENSE command is sent to the device and the requesting
* call back function called.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
static void
ScsiErrorProc(data, callInfoPtr)
ClientData data;
Proc_CallInfo *callInfoPtr;
{
Device *devPtr = (Device *) data;
volatile JaguarIOPB iopbMem;
JaguarCRB crb;
ScsiCmd senseCmd;
char senseBuffer[DEV_MAX_SENSE_BYTES];
DevScsiSenseCmd((ScsiDevice *)devPtr, DEV_MAX_SENSE_BYTES, senseBuffer,
&senseCmd);
FillInScsiIOPB(devPtr, &senseCmd, &iopbMem);
bzero((char *)&crb, sizeof(crb));
MASTER_LOCK(&devPtr->ctrlPtr->mutex);
(void) SendJaguarCmd(devPtr->ctrlPtr, 0, &iopbMem, FILL_IN_CRB_ACTION,
(ClientData) &crb);
MASTER_UNLOCK(&devPtr->ctrlPtr->mutex);
/*
* Ignore the 0x34 ( transfer length mismatch) we're likely to get.
*/
if (crb.iopb.returnStatus & 0xff) {
crb.iopb.returnStatus &= ~0xff;
}
if (crb.iopb.returnStatus != 0) {
(devPtr->frozen.scsiCmdPtr->doneProc)(devPtr->frozen.scsiCmdPtr,
SUCCESS, devPtr->frozen.statusByte,
devPtr->frozen.amountTransferred,
0, (char *) 0);
} else {
(devPtr->frozen.scsiCmdPtr->doneProc)(devPtr->frozen.scsiCmdPtr,
SUCCESS, devPtr->frozen.statusByte,
devPtr->frozen.amountTransferred,
READ_LONG(crb.iopb.maxXferLen), senseBuffer);
}
/*
* Unfreze the workqueue for this device.
*/
MASTER_LOCK(&devPtr->ctrlPtr->mutex);
devPtr->ctrlPtr->memPtr->mcsb.thawQueue =
THAW_WORK_QUEUE(devPtr->workQueue);
MACH_DELAY(100);
MASTER_UNLOCK(&devPtr->ctrlPtr->mutex);
}
/*
*----------------------------------------------------------------------
*
* RequestDone --
*
* Process a request that has finished. Unless a SCSI check condition
* bit is present in the status returned, the request call back
* function is called. If check condition is set we fire off a
* SCSI REQUEST SENSE to get the error sense bytes from the device.
*
* Results:
* None.
*
* Side effects:
* The call back function may be called.
*
*----------------------------------------------------------------------
*/
static void
RequestDone(devPtr,scsiCmdPtr,status,scsiStatusByte,amountTransferred)
Device *devPtr; /* Device for request. */
ScsiCmd *scsiCmdPtr; /* Request that finished. */
ReturnStatus status; /* Status returned. */
unsigned char scsiStatusByte; /* SCSI Status Byte. */
int amountTransferred; /* Amount transferred by command. */
{
if (devJaguarDebug > 3) {
printf("RequestDone for %s status 0x%x scsistatus 0x%x count %d\n",
devPtr->handle.locationName, status,scsiStatusByte,
amountTransferred);
}
/*
* If the request
* suffered an error or the HBA or the scsi status byte
* says there is no error sense present, we can do the
* callback and free the controller.
*/
if ((status != SUCCESS) || !SCSI_CHECK_STATUS(scsiStatusByte)) {
(scsiCmdPtr->doneProc)(scsiCmdPtr, status, scsiStatusByte,
amountTransferred, 0, (char *) 0);
return;
}
/*
* If we got here than the SCSI command came back from the device
* with the CHECK bit set in the status byte. We do this with
* a call back process that can wait for workQueue 0 to become
* available.
*/
devPtr->frozen.scsiCmdPtr = scsiCmdPtr;
devPtr->frozen.statusByte = scsiStatusByte;
devPtr->frozen.amountTransferred = amountTransferred;
Proc_CallFunc(ScsiErrorProc, (ClientData) devPtr, 0);
}
/*
*----------------------------------------------------------------------
*
* ReleaseProc --
*
* Release an attached Jaguar device.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
static ReturnStatus
ReleaseProc(scsiDevicePtr)
ScsiDevice *scsiDevicePtr;
{
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* entryAvailProc --
*
* Act upon an entry becomming available in the queue for this
* controller. This routine is the Dev_Queue callback function that
* is called whenever work becomes available for this controller.
* If the controller is not already busy we dequeue and start the
* request.
*
* Results:
* None.
*
* Side effects:
* Request may be dequeue and submitted to the device. Request callback
* function may be called.
*
*----------------------------------------------------------------------
*/
static Boolean
entryAvailProc(clientData, newRequestPtr)
ClientData clientData; /* Really the Device this request ready. */
List_Links *newRequestPtr; /* The new SCSI request. */
{
register Device *devPtr = (Device *) clientData;
register Controller *ctrlPtr = devPtr->ctrlPtr;
register ScsiCmd *scsiCmdPtr = (ScsiCmd *) newRequestPtr;
Boolean good;
if (devPtr->numActiveCmds >= MAX_CMDS_QUEUED) {
return FALSE;
}
devPtr->numActiveCmds++;
good = SendScsiCommand(devPtr, scsiCmdPtr);
/*
* If the command couldn't be started do the callback function.
*/
if (!good) {
devPtr->numActiveCmds--;
MASTER_UNLOCK(&(ctrlPtr->mutex));
RequestDone(devPtr,scsiCmdPtr,(ReturnStatus)DEV_HARD_ERROR,0,0);
MASTER_LOCK(&(ctrlPtr->mutex));
}
return TRUE;
}
/*
*----------------------------------------------------------------------
*
* StartNextRequest --
*
* Start the next request on the device.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
StartNextRequest(devPtr)
Device *devPtr; /* Device to start request on. */
{
List_Links *newRequest;
while (devPtr->numActiveCmds < MAX_CMDS_QUEUED) {
newRequest = Dev_QueueGetNext(devPtr->handle.devQueue);
if (newRequest == (List_Links *) NIL) {
break;
}
(void) entryAvailProc((ClientData) devPtr, newRequest);
}
}
/*
*----------------------------------------------------------------------
*
* DevJaguarInit --
*
* Check for the existant of the Jaguar HBA controller. If it
* exists allocate data stuctures for it.
*
* Results:
* TRUE if the controller exists, FALSE otherwise.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
ClientData
DevJaguarInit(ctrlLocPtr)
DevConfigController *ctrlLocPtr; /* Controller location. */
{
int ctrlNum;
Controller *ctrlPtr;
short x;
int i;
Address address;
ReturnStatus status;
/*
* See if the controller is there. This controller should occupy
* 2k in the short IO space of the VME.
*/
if (ctrlLocPtr->space != DEV_VME_D16A16) {
panic("Jaguar SCSI HBA %d configured in bad address space %d @ 0x%x\n",
ctrlLocPtr->controllerID, ctrlLocPtr->space, ctrlLocPtr->address);
return DEV_NO_CONTROLLER;
}
address = (Address) ctrlLocPtr->address;
status = Mach_Probe(sizeof(short), address, (char *)&x);
if (status == SUCCESS) {
status = Mach_Probe(sizeof(short), address + 2*1024 - 2,(char *)&x);
}
if (status != SUCCESS) {
if (devJaguarDebug > 3) {
printf("Jaguar # %d not found at address 0x%x\n",
ctrlLocPtr->controllerID, address);
}
return DEV_NO_CONTROLLER;
}
if (!CheckSizes()) {
panic("Jaguar driver structure layout broken\n");
return DEV_NO_CONTROLLER;
}
ctrlNum = ctrlLocPtr->controllerID;
{
Boolean good;
good = InitializeJaguar((volatile JaguarMem *) address,
ctrlLocPtr->name,
VME_INTERRUPT_PRIORITY,
ctrlLocPtr->vectorNumber);
if (!good) {
return DEV_NO_CONTROLLER;
}
}
ctrlPtr = Controllers[ctrlNum] = (Controller *) malloc(sizeof(*ctrlPtr));
bzero((char *) ctrlPtr, sizeof(Controller));
ctrlPtr->memPtr = (JaguarMem *) address;
ctrlPtr->nextCQE = ctrlPtr->memPtr->cmdQueue;
ctrlPtr->workQueue0Busy = FALSE;
ctrlPtr->name = ctrlLocPtr->name;
Sync_SemInitDynamic(&(ctrlPtr->mutex),ctrlPtr->name);
/*
* Initialized the name, device queue header, and the master lock.
* The controller comes up with no devices active and no devices
* attached.
*/
ctrlPtr->devQueues = Dev_CtrlQueuesCreate(&(ctrlPtr->mutex),entryAvailProc);
ctrlPtr->intrLevel = VME_INTERRUPT_PRIORITY;
/*
* Use the same vector for both error and normal completion.
*/
ctrlPtr->intrVector =
JAGUAR_IOPB_INTR_VECTOR(ctrlLocPtr->vectorNumber,
ctrlLocPtr->vectorNumber);
for (i = 0; i < NUM_WORK_QUEUES; i++) {
ctrlPtr->devices[i] = (Device *) NIL;
}
return (ClientData) ctrlPtr;
}
/*
*----------------------------------------------------------------------
*
* DevJaguarAttachDevice --
*
* Attach a SCSI device using the Jaguar HBA.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ScsiDevice *
DevJaguarAttachDevice(devicePtr, insertProc)
Fs_Device *devicePtr; /* Device to attach. */
void (*insertProc) _ARGS_ ((List_Links *elementPtr,
List_Links *elementListHdrPtr));
/* Queue insert procedure. */
{
Device *devPtr;
Controller *ctrlPtr;
char tmpBuffer[512];
int ctrlNum;
int targetID, lun, bus;
int i, workQueue;
/*
* First find the Jaguar controller this device is on. For the Jaguar
* we really have 2 HBA per real controller because each HBA has
* two SCSI buses.
*/
ctrlNum = SCSI_HBA_NUMBER(devicePtr)/2;
if ((ctrlNum > MAX_JAGUAR_CTRLS) ||
(Controllers[ctrlNum] == (Controller *) 0)) {
return (ScsiDevice *) NIL;
}
ctrlPtr = Controllers[ctrlNum];
/*
* See if the device is already present.
*/
targetID = SCSI_TARGET_ID(devicePtr);
lun = SCSI_LUN(devicePtr);
bus = SCSI_HBA_NUMBER(devicePtr) & 0x1;
MASTER_LOCK(&(ctrlPtr->mutex));
/*
* See if we already have a work queue setup for this device. Also,
* find a unsed workQueue to allocate for this device.
*/
workQueue = -1;
for (i = 0; i < NUM_WORK_QUEUES; i++) {
if (ctrlPtr->devices[i] != (Device *) NIL) {
if ((ctrlPtr->devices[i]->targetID == targetID) &&
(ctrlPtr->devices[i]->bus == bus)) {
if (ctrlPtr->devices[i]->handle.LUN == lun) {
MASTER_UNLOCK(&(ctrlPtr->mutex));
return (ScsiDevice *) (ctrlPtr->devices[i]);
}
/*
* The same targetID and a different LUN doesn't work.
*/
MASTER_UNLOCK(&(ctrlPtr->mutex));
printf("Warning: %s: 4210 only supports one LUN per target.\n",
ctrlPtr->name);
printf("%s: Target %d LUN %d is already present.\n",
ctrlPtr->name,
ctrlPtr->devices[i]->targetID,
ctrlPtr->devices[i]->handle.LUN);
return (ScsiDevice *) NIL;
}
} else {
/*
* Record the first unsed workQueue.
*/
if (workQueue == -1) {
workQueue = i+1;
}
}
}
MASTER_UNLOCK(&(ctrlPtr->mutex));
if (workQueue == -1) {
printf("%s: Too many devices attached.\n", ctrlPtr->name);
return (ScsiDevice *) NIL;
}
if (!InitializeWorkq(ctrlPtr, workQueue, FALSE, 1)) {
return (ScsiDevice *) NIL;
}
ctrlPtr->devices[workQueue-1] = devPtr =
(Device *) malloc(sizeof(Device));
bzero((char *) devPtr, sizeof(Device));
devPtr->handle.devQueue = Dev_QueueCreate(ctrlPtr->devQueues,
0, insertProc, (ClientData) devPtr);
(void) sprintf(tmpBuffer, "%s Bus %d Target %d LUN %d", ctrlPtr->name,
bus, targetID, lun);
devPtr->handle.locationName =
(char *) strcpy(malloc(strlen(tmpBuffer)+1),tmpBuffer);
devPtr->handle.LUN = lun;
devPtr->handle.releaseProc = ReleaseProc;
devPtr->handle.maxTransferSize = DEV_MAX_DMA_SIZE;
devPtr->bus = bus;
devPtr->targetID = targetID;
devPtr->unitAddress = JAGUAR_UNIT_ADDRESS(bus, targetID, lun);
devPtr->workQueue = workQueue;
devPtr->ctrlPtr = ctrlPtr;
return (ScsiDevice *) devPtr;
}